#include<stdio.h>
#include<sys/types.h>
#include<wait.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
#include<ctype.h>
#include <sys/stat.h>
#include <fcntl.h>


#define SIZE 512
#define SEP " "
#define NUM 32
#define ZERO '\0';
#define SkipPath(p) do{ p+=(strlen(p)-1);  while(*p!='/') p--;}while(0)
#define SkipSpace(Command,pos) do{while(isspace(Command[pos]))pos++;}while(0)
#define None_Redir 0  //无重定向
#define Out_Redir 1  //输出重定向
#define App_Redir 2  //追加重定向
#define In_Redir  4  //输入重定向
#define mode 0666
const char* filename=NULL;
//重定向方式  
int Redir=0;    

char* Argv[NUM];
int lastcode=0;
char CWD[SIZE*2];
void Die()
{
  exit(1);
}
const char* Getusername()
{
  const char* username=getenv("USER");
  return username;
}

const char* GetHostName()
{
 const char* hostname=getenv("HOSTNAME"); 
 return hostname;
}

const char* GetPwd()
{
  const char* pwd=getenv("PWD");
  return pwd;
}

void MakeCommandLine()
{
  const char* username=Getusername();
  const char* hostname=GetHostName();
  const char* pwd=GetPwd();
  char Line[SIZE];
  SkipPath(pwd);
  snprintf(Line,sizeof(Line),"[%s@%s %s]>",username,hostname,strlen(pwd)==1?"/":pwd+1);
  printf("%s",Line);
}

size_t GetCommand(char Command[],size_t n)
{
  char* s= fgets(Command,n,stdin);
  if(s==NULL)
    return -1;
  Command[strlen(Command)-1]=ZERO;
  return strlen(Command);
}

void SplitCommand(char Command[],size_t n)
{
  (void) n;
  Argv[0]=strtok(Command,SEP);
  int index=1;
  while((Argv[index++]=strtok(NULL,SEP)));
}

void CD()
{
  const char* path=Argv[1];
  if(path==NULL) path=getenv("HOME");
  chdir(path);
  //更新环境变量
  char temp[SIZE*2];
  getcwd(temp,sizeof(temp));
  snprintf(CWD,sizeof(CWD),"PWD=%s",temp);
  putenv(CWD);
}

int CheckBuildin()
{
  int flag=0;
  const char* Enter_cmd=Argv[0];
  if(strcmp(Enter_cmd,"cd")==0)
  {
    flag=1;
    CD();
  }
  else if(strcmp(Enter_cmd,"echo")==0&&strcmp(Argv[1],"$?")==0)
   {
     flag=1;
     printf("%d\n",lastcode);
   }
  
  return flag;
}

void Check_Redir(char Command[])
{
  int i=0;
  int len=strlen(Command);
  while(i<len)
  {
    if(Command[i]=='>')
    {
     if(Command[i+1]=='>')
     {
      Command[i++]=ZERO;
      i++;
      Redir=App_Redir;
      SkipSpace(Command,i);
      filename=Command+i;
     }
     else 
     {
      Command[i++]=ZERO;
      Redir=Out_Redir;
      SkipSpace(Command,i);
      filename=Command+i;
     }
    }
    else if(Command[i]=='<')
    {
      Command[i++]=ZERO;
      Redir=In_Redir;
      SkipSpace(Command,i);
      filename=Command+i;
    }
    else 
    {
      i++;
    }
  }
}

void ExecuteCommand()
{
  pid_t id=fork();
  if(id<0)
    Die();
  if(id==0)
  {
    //子进程
   //重定向设置
   if(filename!=NULL)
   {
     if(Redir==In_Redir)
     {
       int fd=open(filename,O_RDONLY);
       dup2(fd,0);
     }
     else if(Redir==Out_Redir)
     {
       int fd=open(filename,O_WRONLY|O_CREAT|O_TRUNC,mode);
       dup2(fd,1);
     }
     else if(Redir==App_Redir)
     {
       int fd=open(filename,O_WRONLY|O_CREAT|O_APPEND,mode);
       dup2(fd,1);
     }
     else{}
   }
   //检查是否是内置命令
   int flag=0;
   flag=CheckBuildin();
  //若果是内置命令后续就不要执行了
   if(flag==0)
   {
     execvp(Argv[0],Argv);
     exit(errno);
   }
  }
  else 
  {
    int status=0;
    pid_t rid=waitpid(id,&status,0);
    if(rid>0)
    {
      lastcode=WEXITSTATUS(status);
      //错误码不为0就报错
      if(lastcode!=0)
        printf("%s:%s:%d\n",Argv[0],strerror(lastcode),lastcode);
    }
  }
}

int main()
{
  while(1)
  {
    //重定向信息初始化
    filename=NULL;
    //重定向方式    
    Redir=0;    

   //打印命令行提示信息
   MakeCommandLine();
   //获取指令信息
   char Command[SIZE];
   int n= GetCommand(Command,sizeof(Command));
    if(n<=0)
     continue;
    //检查是否重定向
    Check_Redir(Command);
   //分割指令
   SplitCommand(Command,sizeof(Command));
   //执行命令
   ExecuteCommand();
  }
  return 0;
}
